from redis import asyncio as aioredis
from redis.asyncio.client import Pipeline
from typing import Any, Dict, List, Tuple, Any
from datetime import timedelta
from enum import Enum


class ConnectType(Enum):
    Url: int = 1
    Default: int = 2


class Redis(object):
    def __init__(
            self,
            config: Dict[str, Any],  # redis配置
            pre_key: str = "",  # key前缀
            pipeline: Pipeline = None,  # 管道
            connection_type: ConnectType = ConnectType.Default
    ):
        self.config = config
        self.pre_key = pre_key  # key的前缀
        if pipeline:
            self.redis = pipeline
        else:
            if connection_type == ConnectType.Default:
                self.redis = aioredis.client.Redis(**self.config, decode_responses=True)
            else:
                self.redis = aioredis.client.Redis.from_url(
                    self.format_url(),
                    decode_responses=True
                )

    def format_url(self):
        """格式化url"""
        config = self.config
        return f"redis://{config['username']}:{config['password']}@{config['host']}:{config['port']}/{config['db']}"

    def new_pipeline(self):
        """new一个管道，继承自Redis，避免重复复写命令"""
        return RedisPipeline(self.pre_key, self.redis.pipeline())

    def convert_key(self, key: str):
        return f"{self.pre_key}_{key}"

    def get(self, key: str):
        return self.redis.get(self.convert_key(key))

    def hgetall(self, key: str):
        return self.redis.hgetall(self.convert_key(key))

    def hget(self, key: str, field: str):
        return self.redis.hget(self.convert_key(key), field)

    def set(
            self,
            key: str,
            val: Any,
            ex: None | float | timedelta = None,
            px: None | float | timedelta = None,
            **kwargs
    ):
        return self.redis.set(self.convert_key(key), val, ex=ex, px=px, **kwargs)

    def delete(self, *keys):
        return self.redis.delete(*[self.convert_key(key) for key in keys])

    def hset(self, key: str, field: str, val: Any, **kwargs):
        return self.redis.hset(self.convert_key(key), field, val, **kwargs)

    def hmset(self, key: str, field2val: Dict[str, Any]):
        return self.redis.hmset(self.convert_key(key), field2val)

    def hdel(self, key: str, *fields):
        return self.redis.hdel(self.convert_key(key), *fields)

    def pubsub(self, **kwargs):
        return self.redis.pubsub(**kwargs)

    def publish(self, key: str, data: Any):
        return self.redis.publish(key, data)


class RedisPipeline(Redis):
    def __init__(self, pre_key: str, pipeline: Pipeline):
        super().__init__({}, pre_key=pre_key, pipeline=pipeline)

    def get_instance(self) -> Pipeline:
        assert isinstance(self.redis, Pipeline)
        return self.redis

    async def execute(self):
        return await self.get_instance().execute()
